/**
 * @(#)ASTUtil.java, 2008-4-30.
 * Copyright 2008  Program Analysis Research Group(PARG),Institute of Software, Peking University.
 * All rights reserved.
 */
package edu.pku.sei.pgie.analyzer.util;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.WildcardType;

/**
 * @author HeLi
 */
public class ASTUtil {
	public static String getTypeName(Type type) {
		if (type.isPrimitiveType()) {
			return ((PrimitiveType) type).getPrimitiveTypeCode().toString();
		}

		if (type.isArrayType()) {
			ArrayType arrayType = (ArrayType) type;
			StringBuffer typeName = new StringBuffer();
			typeName.append(getTypeName(arrayType.getElementType()));
			for (int i = 0; i < arrayType.getDimensions(); i++) {
				typeName.append("[]");
			}
			return typeName.toString();
		}

		if (type.isQualifiedType()) {
			return ((QualifiedType) type).getName().getFullyQualifiedName();
		}

		if (type.isSimpleType()) {
			return ((SimpleType) type).getName().getFullyQualifiedName();
		}

		if (type.isParameterizedType()) {
			ParameterizedType parameterizedType = (ParameterizedType) type;
			StringBuffer typeName = new StringBuffer();
			typeName.append(getTypeName(parameterizedType.getType()));
			typeName.append("<");
			List typeArguments = parameterizedType.typeArguments();
			for (Iterator iter = typeArguments.iterator(); iter.hasNext();) {
				Type typeArgument = (Type) iter.next();
				typeName.append(getTypeName(typeArgument));
				if (iter.hasNext()) {
					typeName.append(",");
				}
			}
			typeName.append(">");
			return typeName.toString();
		}

		if (type.isWildcardType()) {
			WildcardType wildcardType = (WildcardType) type;
			StringBuffer typeName = new StringBuffer();
			typeName.append("? ");
			typeName.append(wildcardType.isUpperBound() ? "extends" : "super");
			typeName.append(" " + getTypeName(wildcardType.getBound()));
			return typeName.toString();
		}

		throw new RuntimeException("invalid type");
	}

    public static ICompilationUnit[] collectCompilationUnit(
            IPackageFragmentRoot src) throws JavaModelException {
        IJavaElement[] elements = src.getChildren();
        List<IJavaElement> cus = new ArrayList<IJavaElement>();
        for (int i = 0; i < elements.length; i++) {
            switch (elements[i].getElementType()) {
                case IJavaElement.COMPILATION_UNIT:
                    cus.add(elements[i]);
                    break;
                case IJavaElement.PACKAGE_FRAGMENT:
                    ICompilationUnit[] units = ((IPackageFragment) elements[i])
                            .getCompilationUnits();
                    for (int j = 0; j < units.length; j++) {
                        cus.add(units[j]);
                    }
                    break;
            }
        }
        ICompilationUnit[] copy = new ICompilationUnit[cus.size()];
        System.arraycopy(cus.toArray(), 0, copy, 0, cus.size());
        return copy;
    }

    public static ASTNode[] findInSubTree(ASTNode root, final Class[] targets) {
        final List<ASTNode> list = new ArrayList<ASTNode>();
        ASTVisitor visitor = new ASTVisitorTemplate() {
            public void visitStub(ASTNode node) {
                if (targets == null) {
                    list.add(node);
                    return;
                }
                for (int i = 0; i < targets.length; i++) {
                    if (targets[i].isInstance(node)) {
                        list.add(node);
                    }
                }
            }
        };
        root.accept(visitor);
        ASTNode[] nodes = new ASTNode[list.size()];
        Iterator iterator = list.iterator();
        int i = 0;
        while (iterator.hasNext()) {
            nodes[i++] = (ASTNode) iterator.next();
        }
        return nodes;
    }

    public static String getRefrenceSiteBinding(QualifiedName qName) {
        Name qualifier = qName.getQualifier();
        if (qualifier == null)
            return null;
        IBinding binding = qualifier.resolveBinding();
        if (binding == null)
            return null;
        if (binding instanceof ITypeBinding) {
            return ((ITypeBinding) binding).getQualifiedName();
        }
        if (binding instanceof IVariableBinding) {
            return ((IVariableBinding) binding).getType().getQualifiedName();
        }
        return null;
    }

    public static ASTNode assertChild(ASTNode node, Class target) {
        while (!(node instanceof CompilationUnit)) {
            if (target.isInstance(node.getParent()))
                return node.getParent();
        }
        return null;
    }

    public static String getComponentTypeName(ITypeBinding typeBinding) {
    	if(typeBinding == null)
    		return null;
    	if(typeBinding.isArray()) {
    		return getComponentTypeName(typeBinding.getComponentType());
    	} else {
    		return typeBinding.getQualifiedName();
    	}
    }

    public static String getContainerValueType(ITypeBinding typeBinding) {
        if(typeBinding == null) {
            return null;
        }
        if(typeBinding.isArray()) {
            return getContainerValueType(typeBinding.getComponentType());
        } else {
            if(typeBinding.isGenericType()) {
                ITypeBinding[] bindings = typeBinding.getTypeArguments();
                if(bindings.length > 1) {
                    return getContainerValueType(bindings[1]);
                } else if(bindings.length > 0) {
                    return getContainerValueType(bindings[0]);
                }
            }
            return typeBinding.getQualifiedName();
        }
    }

}
